package basicobject

import (
	"bytes"
	"strconv"
	"yizhuoyan/go-study/pdfgo/characterset"
	"yizhuoyan/go-study/pdfgo/handyio"
)

type ByteString struct {
	value []byte
}

func (this *ByteString) GetValue() []byte {
	return this.value
}
func (this *ByteString) Serialize(w handyio.PdfRawWriter) error {
	w.WriteByte('(')
	w.WriteBytes(this.value)
	w.WriteByte(')')
	return nil
}
func (this *ByteString) UnSerialize(r handyio.PdfRawReader) (e error) {
	if !r.NextByteEquals('(') {
		return r.NotSupport("非(开头")
	}
	defer func(startAt int64) {
		if e != nil {
			_ = r.IndexMoveTo(startAt)
		}
	}(r.IndexCurrent())

	var rst = new(bytes.Buffer)
	parenthesesCount := 1
	for parenthesesCount > 0 {
		if r.NextByteEquals('(') {
			parenthesesCount++
			rst.WriteByte('(')
			continue
		}
		if r.NextByteEquals(')') {
			parenthesesCount--
			if parenthesesCount > 0 {
				rst.WriteByte(')')
			}
			continue
		}
		if r.NextByteEquals('\\') {
			if err := this.UnSerializeEscapeBytes(r, rst); err != nil {
				return nil
			}
			continue
		}
		nextByte, err := r.NextByte()
		if err != nil {
			return err
		}
		rst.WriteByte(nextByte)
	}
	this.value = rst.Bytes()

	return nil
}

func (this *ByteString) UnSerializeEscapeBytes(r handyio.PdfRawReader, rst *bytes.Buffer) error {
	nextByte, err := r.NextByte()
	if err != nil {
		return err
	}
	switch nextByte {
	case 'n':
		rst.WriteByte('\n')
	case 'r':
		rst.WriteByte('\r')
	case 't':
		rst.WriteByte('\t')
	case 'b':
		rst.WriteByte('\b')
	case 'f':
		rst.WriteByte('\f')
	case '(':
		rst.WriteByte('(')
	case ')':
		rst.WriteByte(')')
	case '\\':
		rst.WriteByte('\\')
	default: //octal \dd
		if err = this.nextEscapeOctalByte(r, rst); err != nil {
			return err
		}
	}
	return nil
}

func (this *ByteString) nextEscapeOctalByte(r handyio.PdfRawReader, rst *bytes.Buffer) error {
	octal3 := r.NextBytes(func(b byte) int {
		if characterset.IsOctalChar(b) {
			return handyio.ReadNext
		}
		if characterset.IsWhiteSpaceChar(b) {
			return handyio.ReadByteSkip
		}
		return handyio.ReadStop
	})
	if len(octal3) == 0 {
		return nil
	}
	unicode, err := strconv.ParseInt(string(octal3), 8, 8)
	if err != nil {
		return err
	}
	rst.WriteByte(byte(unicode))
	return nil
}
